package bll

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/golang-module/carbon/v2"
	"gitlab.local/backend/proto/dto"
	"gitlab.local/backend/proto/manager"
	"gorm.io/gorm"
	"simcard/bll/upiot"
	"simcard/client/grpc"
	log "simcard/collector/logger"
	"simcard/utils"

	"simcard/model"
	"simcard/model/entity"
	"simcard/model/mapping"
	"simcard/store"
	"simcard/store/postgres"
	"time"
)

type simCard struct {
	iSimCard               store.ISimCard
	iPrepaid               store.IPrepaid
	iSimCardRechargeRecord store.ISimCardRechargeRecord
}

var SimCard = &simCard{
	iSimCard:               postgres.SimCard,
	iPrepaid:               postgres.Prepaid,
	iSimCardRechargeRecord: postgres.SimCardRechargeRecord,
}

func init() {
	Register(SimCard)
}

func (a *simCard) init() func() {
	return func() {}
}

// Create
func (a *simCard) Create(ctx context.Context, in *model.SimCardCreateRequest) error {
	var (
		err error
	)
	c := buildSimCard(in)
	_, err = a.iSimCard.Create(ctx, c)
	return err
}

// CreateInBatch
func (a *simCard) CreateInBatch(ctx context.Context, ins []*model.SimCardCreateRequest, c int) error {

	var (
		err error
		es  []*entity.SimCard
	)

	for _, in := range ins {
		es = append(es, buildSimCard(in))
	}

	err = a.iSimCard.CreateInBatch(ctx, es, c)
	for i, _ := range es {
		ins[i].Id = es[i].Id
	}
	return err
}

func (a *simCard) syncCardConsumptionInfo(ctx context.Context, iccid2IdMap map[string]int64, iccids ...string) error {

	for _, req := range utils.SplitSlice(iccids, 50) {
	retry:
		respCardList, resp, e := SimCardAgent.UPIot.ListCardsInfo(req)
		if e != nil {
			if resp != nil && resp.Code == upiot.FrequencyLimited {
				<-time.After(time.Second * 3)
				goto retry
			}
			return e
		}
		if len(respCardList) == 0 {
			return fmt.Errorf("查询卡信息失败,没有这张卡的信息")
		}

		detail := map[string]any{}
		for _, v := range respCardList {

			detail["status"] = upiot.UpiotCardStatus(v.AccountStatus)
			updateRequest := &model.SimCardUpdateRequest{
				Id:            utils.GetValuePointer(iccid2IdMap[v.Iccid]),
				EffectiveDate: utils.GetValuePointer(v.ValidDate),
				ExpiredDate:   utils.GetValuePointer(v.ExpiryDate),
				Status:        utils.GetValuePointer(int(model.NewCardStatus(model.AgentUPIOT, upiot.UpiotCardStatus(v.AccountStatus)))),
			}

			if err := a.Update(ctx, updateRequest); err != nil {
				log.Errorf("更新流量卡%v的流量信息失败%+v", v.Iccid, err)
			}
		}
	}
	return nil
}

func (a *simCard) SyncCardConsumptionInfo(ctx context.Context, in *model.SimCardConsumptionInfoCronRequest) error {

	var (
		size           = 400
		iccids         []string
		simCardRequest = &model.SimCardListRequest{
			Index: 1,
			Size:  size,
		}
		iccid2IdMap = map[string]int64{}
	)

	ctx, _ = context.WithTimeout(ctx, time.Minute*3)
	simCardRequest.EbikeIds = in.Ebikes
	simCardRequest.Ids = in.Ids
	simCardRequest.Id = in.Id
	if len(in.Ids) > len(in.Ebikes) {
		size = len(in.Ids)
	} else {
		size = len(in.Ebikes)
	}
	if size > simCardRequest.Size {
		simCardRequest.Size = size
	}

	_, respSimCards, err := a.iSimCard.List(ctx, simCardRequest, model.QueryOptions{OnlyList: true, Conditions: []string{
		"iccid != ''",
	}})
	if err != nil {
		return err
	}

	if len(respSimCards) <= 0 {
		return fmt.Errorf("ICCID为空，无法同步")
	}
	for _, v := range respSimCards {
		var agentDetail = &model.AgentDetail{}
		_ = json.Unmarshal(v.AgentDetail, agentDetail)
		if model.AgentType(agentDetail.Code) != model.AgentUPIOT {
			continue
		}
		iccids = append(iccids, v.Iccid)
		iccid2IdMap[v.Iccid] = v.Id
	}

	return a.syncCardConsumptionInfo(ctx, iccid2IdMap, iccids...)
}

func (a *simCard) Sync(ctx context.Context, in *model.SyncRequest) error {

	var (
		ebikeService = grpc.EbkieService
		size         = int32(200)
		ebikeRequest = &manager.GetEbikeListReq{
			Index: 1,
			Size:  size,
		}
		totalLoadCount int
	)

	for {

		var (
			upsertes []*model.SimCardUpsertRequest
		)

		if in.DataType == model.DataFetchTypePull {
			ctx, _ = context.WithTimeout(ctx, time.Minute*3)
			if in.DataRange == model.SimCardSyncDataPartOf {
				for _, v := range in.Data {
					ebikeRequest.BikeIds = append(ebikeRequest.BikeIds, v.EbikeId)
				}
				ebikeRequest.Size = int32(len(ebikeRequest.BikeIds))
			}

			respEbike, err := ebikeService.GetEbikeList(ctx, ebikeRequest)
			if err != nil {
				return err
			}

			var (
				uu            = make([]*model.SimCardUpsertRequest, len(respEbike.List))
				userIds       []int64
				userIdDup     = map[int64]struct{}{}
				userId2Indexs = map[int64][]int{}
			)
			for i, v := range respEbike.List {

				if v.UserId > 0 {
					if _, ok := userIdDup[v.UserId]; !ok {
						userIdDup[v.UserId] = struct{}{}
						userIds = append(userIds, v.UserId)
					}

					userId2Indexs[v.UserId] = append(userId2Indexs[v.UserId], i)
				}

				uu[i] = &model.SimCardUpsertRequest{
					EbikeId:   v.BikeId,
					UserId:    utils.GetValuePointer(v.UserId),
					Sn:        utils.GetValuePointer(v.SnCode),
					FrameCode: utils.GetValuePointer(v.FrameCode),
					Iccid:     utils.GetValuePointer(v.Iccid),
				}
			}

			respUsers, err := grpc.UserService.UserListInfo(ctx, &dto.UserListInfoRequest{Ids: userIds})
			if err != nil {
				log.Error(fmt.Sprintf("获取车辆用户信息失败:%+v", err))
			} else {
				for _, v := range respUsers.List {

					for index, _ := range userId2Indexs[v.Id] {
						uu[index].UserPhone = utils.GetValuePointer(v.Phone)
					}
				}
			}

			upsertes = uu
		}

		if in.DataType == model.DataFetchTypePush {
			upsertes = in.Data
		}

		err := a.Upsert(ctx, upsertes...)
		if err != nil {
			return err
		}

		if in.DataRange == model.SimCardSyncDataPartOf {
			break
		}

		totalLoadCount += len(upsertes)
		if in.DataRange == model.SimCardSyncDataAll {
			if len(upsertes) < int(ebikeRequest.Size) {
				break
			}
			ebikeRequest.Index = ebikeRequest.Index + 1
		}
	}

	log.Info(fmt.Sprintf("总共同步了%v条车辆数据", totalLoadCount))
	return nil
}

/* 查询simcard 服务状态*/
func (a *simCard) GetSimCardServiceDetail(ctx context.Context, in *model.GetSimCardServiceDetailRequest) (*model.GetSimCardServiceDetailResponse, error) {

	/* 有预付款，就拿 Prepaid 表的状态 */
	_, respPrepaid, err := a.iPrepaid.List(ctx, &model.PrepaidListRequest{
		Index:    1,
		Size:     1,
		Statuses: []int{int(model.RepaidStatusDelay), int(model.RepaidStatusRightNow)},
		EbikeId:  utils.GetValuePointer(in.EBikeId),
	}, model.QueryOptions{OnlyList: true, OrderFields: model.NewOrderFields(model.NewOrderField("id", model.OrderByDESC))})
	if err != nil {
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, err
		}
	}

	if len(respPrepaid) > 0 {
		rp := respPrepaid[0]
		result := &model.GetSimCardServiceDetailResponse{
			EBikeId:   in.EBikeId,
			StartDate: rp.EffectiveDate,
			EndDate:   rp.ExpiredDate,
			Status:    model.CardStatusInValidityPeriod,
		}
		return result, nil
	}

	_, respSimCard, err := a.iSimCard.List(ctx, &model.SimCardListRequest{
		Index:   1,
		Size:    1,
		EbikeId: utils.GetValuePointer(in.EBikeId),
	}, model.QueryOptions{OnlyList: true})
	if err != nil {
		return nil, err
	}

	if len(respSimCard) > 0 {
		rs := respSimCard[0]
		result := &model.GetSimCardServiceDetailResponse{
			EBikeId:   in.EBikeId,
			Status:    model.CardStatus(rs.Status),
			StartDate: rs.EffectiveDate,
			EndDate:   rs.ExpiredDate,
		}
		return result, nil
	}

	return &model.GetSimCardServiceDetailResponse{EBikeId: in.EBikeId}, nil
}

func (a *simCard) PreRecharge(ctx context.Context, ins *model.PreRechargeRequest) error {

	var (
		ids              []int64
		ebikeIds         []int64
		id2PrePidId      = map[int64]int64{}
		ebikeid2PrePidId = map[int64]int64{}
	)
	for _, in := range ins.Items {

		if in.SimCardId != nil && *in.SimCardId > 0 {
			ids = append(ids, *in.SimCardId)
			if in.PrepaidId != nil && *in.PrepaidId > 0 {
				id2PrePidId[*in.SimCardId] = *in.PrepaidId
			}
		}

		if in.EbikeId != nil && *in.EbikeId > 0 {
			ebikeIds = append(ebikeIds, *in.EbikeId)
			if in.PrepaidId != nil && *in.PrepaidId > 0 {
				ebikeid2PrePidId[*in.EbikeId] = *in.PrepaidId
			}
		}

	}
	_, respSimCard, err := a.iSimCard.List(ctx, &model.SimCardListRequest{
		Ids:      ids,
		EbikeIds: ebikeIds,
	}, model.QueryOptions{OnlyList: true})
	if err != nil {
		return err
	}
	var recordRequests []*model.SimCardRechargeRecordCreateRequest
	for _, v := range respSimCard {

		request := &model.SimCardRechargeRecordCreateRequest{
			EbikeId:     v.EbikeId,
			CardId:      v.Id,
			AgentId:     v.AgentId,
			AgentDetail: v.AgentDetail,
			Iccid:       v.Iccid,
			Status:      int(model.RechargeRecordQueue),
		}
		if vv, ok := id2PrePidId[v.Id]; ok {
			request.PrepaidId = utils.GetValuePointer(vv)
		}

		if vv, ok := ebikeid2PrePidId[v.EbikeId]; ok {
			request.PrepaidId = utils.GetValuePointer(vv)
		}

		recordRequests = append(recordRequests, request)
	}

	return SimCardRechargeRecord.CreateInBatch(ctx, ins.Mode, recordRequests, 200)
}

func (a *simCard) Recharge(ctx context.Context, in *model.RechargeRequest) (upiot.ChargeForCardDataResult, *upiot.Response, error) {

	return SimCardAgent.UPIot.RechargeForCardData(in.Iccid)
}

func (a *simCard) mergeDetail(ctx context.Context, id int64, detail json.RawMessage) (json.RawMessage, error) {

	respSimCard, err := a.iSimCard.Find(ctx, &model.SimCardInfoRequest{Id: id})
	if err != nil {
		return detail, err
	}

	var simCardDetail = map[string]any{}
	_ = json.Unmarshal(respSimCard.Detail, &simCardDetail)

	_ = json.Unmarshal(detail, &simCardDetail)

	return json.Marshal(simCardDetail)
}

/*
Upsert 更新流量卡的基本信息(iccid,frame_code,user_phone)的同时，根据iccid获取流量卡信息(状态，到期时间，生效时间)
* 如果新创建的流量卡，那么直接根据iccid获取流量卡信息
* 如果更新创建的流量卡，那么判断流量卡的iccid是否有变更，如果没有变更，那么不用获取，否则根据变更的iccid获取流量卡信息
*/
func (a *simCard) Upsert(ctx context.Context, ins ...*model.SimCardUpsertRequest) error {

	var (
		ebikeIds []int64
		ebikeMap = map[int64]*entity.SimCard{}
	)

	for _, in := range ins {
		ebikeIds = append(ebikeIds, in.EbikeId)
	}

	_, respSimCards, err := a.iSimCard.List(ctx, &model.SimCardListRequest{
		Index:    1,
		Size:     len(ebikeIds),
		EbikeIds: ebikeIds,
	}, model.QueryOptions{OnlyList: true})
	if err != nil {
		return err
	} else {
		for _, sc := range respSimCards {
			ebikeMap[sc.EbikeId] = sc
		}
	}

	var (
		insertRequests []*model.SimCardCreateRequest
		updateRequests []*model.SimCardUpdateRequest
		iccids         []string
		iccid2IdMap    = map[string]int64{}
	)
	for _, in := range ins {
		if v, ok := ebikeMap[in.EbikeId]; !ok {
			insertRequests = append(insertRequests, in.ToSimCardCreateRequest())
		} else {
			updateRequests = append(updateRequests, &model.SimCardUpdateRequest{
				Id:        utils.GetValuePointer(v.Id),
				EbikeId:   utils.GetValuePointer(v.EbikeId),
				UserId:    in.UserId,
				UserPhone: in.UserPhone,
				AgentId:   in.AgentId,
				Sn:        in.Sn,
				FrameCode: in.FrameCode,
				Iccid:     in.Iccid,
			})
		}
	}

	if len(insertRequests) > 0 {
		if err = a.CreateInBatch(ctx, insertRequests, 200); err != nil {
			log.Error("创建 simcard 信息失败")
		} else {
			for _, req := range insertRequests {
				if req.Iccid != "" {
					iccid2IdMap[req.Iccid] = req.Id
					iccids = append(iccids, req.Iccid)
				}
			}
		}
	}

	if len(updateRequests) > 0 {
		for _, updateRequest := range updateRequests {
			if err = a.Update(ctx, updateRequest); err != nil {
				log.Error("更新 simcard 信息失败")
			} else {

				if old, ok := ebikeMap[*updateRequest.EbikeId]; ok {
					if old.Iccid != *updateRequest.Iccid && updateRequest.Iccid != nil && *updateRequest.Iccid != "" {
						iccid2IdMap[*updateRequest.Iccid] = *updateRequest.Id
						iccids = append(iccids, *updateRequest.Iccid)
					}
				}
			}
		}
	}

	return a.syncCardConsumptionInfo(ctx, iccid2IdMap, iccids...)
}

// Update
func (a *simCard) Update(ctx context.Context, in *model.SimCardUpdateRequest) error {
	var (
		dict = make(map[string]interface{})
	)

	if in.Id != nil {
		dict["id"] = in.Id
	}

	if in.UserId != nil {
		dict["user_id"] = in.UserId
	}

	if in.EbikeId != nil {
		dict["ebike_id"] = in.EbikeId
	}

	if in.UserPhone != nil {
		dict["user_phone"] = in.UserPhone
	}

	if in.AgentId != nil {
		dict["agent_id"] = in.AgentId
	}

	if in.Sn != nil {
		dict["sn"] = in.Sn
	}

	if in.FrameCode != nil {
		dict["frame_code"] = in.FrameCode
	}

	if in.FrameCode != nil {
		dict["iccid"] = in.Iccid
	}

	if in.EffectiveDate != nil {
		dict["effective_date"] = in.EffectiveDate

		dict["effective_date_unix"] = carbon.ParseByFormat(*in.EffectiveDate, "2006-01-02", carbon.PRC).Timestamp()
	}

	if in.ExpiredDate != nil {

		dict["expired_date"] = in.ExpiredDate
		dict["expired_date_unix"] = carbon.ParseByFormat(*in.ExpiredDate, "2006-01-02", carbon.PRC).Timestamp()
	}

	if in.Status != nil {
		dict["status"] = in.Status
	}

	if in.Detail != nil {

		dict["detail"], _ = a.mergeDetail(ctx, *in.Id, in.Detail)
	}

	if in.CreatedAt != nil {
		dict["created_at"] = in.CreatedAt
	}

	if in.UpdatedAt != nil {
		dict["updated_at"] = in.UpdatedAt
	}

	// do other update here
	updateAt := time.Now().Unix()
	in.UpdatedAt = &updateAt
	return a.iSimCard.Update(ctx, *in.Id, dict)
}

// Delete
func (a *simCard) Delete(ctx context.Context, in *model.SimCardDeleteRequest) error {
	return a.iSimCard.Delete(ctx, in)
}

func (a *simCard) UpdateExpiringStatus(ctx context.Context, in *model.SimCardUpdateExpiringRequest) error {

	var (
		size         = 500
		ebikeRequest = &model.SimCardListRequest{
			Status:   utils.GetValuePointer(int(model.CardStatusInValidityPeriod)),
			Expiring: utils.GetValuePointer(true),
			Index:    1,
			Size:     size,
		}
	)

	for {

		var (
			updateIds []int64
		)
		ctx, _ = context.WithTimeout(ctx, time.Minute*3)
		if in.DataRange == model.SimCardSyncDataPartOf {
			for _, v := range in.Data {
				ebikeRequest.EbikeIds = append(ebikeRequest.EbikeIds, v.EbikeId)
			}
			ebikeRequest.Size = len(ebikeRequest.EbikeIds)
		}

		respSimCards, err := a.List(ctx, ebikeRequest, model.QueryOptions{OnlyList: true})
		if err != nil {
			return err
		}
		for _, v := range respSimCards.List {
			updateIds = append(updateIds, v.Id)
		}

		for _, updateId := range updateIds {
			_ = a.Update(ctx, &model.SimCardUpdateRequest{
				Id:     utils.GetValuePointer(updateId),
				Status: utils.GetValuePointer(int(model.CardStatusExpiring)),
			})
		}

		if in.DataRange == model.SimCardSyncDataPartOf {
			break
		}

		if in.DataRange == model.SimCardSyncDataAll {
			if len(respSimCards.List) < ebikeRequest.Size {
				break
			}
			ebikeRequest.Index = ebikeRequest.Index + 1
		}
	}

	return nil

}

func (a *simCard) UpdateActiveStatus(ctx context.Context, in *model.UpdateActiveRequest) error {

	var (
		size         = 300
		ebikeRequest = &model.SimCardListRequest{
			Status: utils.GetValuePointer(int(model.CardStatusGracePeriod)),
			Index:  1,
			Size:   size,
		}
	)

	for {

		var (
			iccid2IdMap = map[string]int64{}
			iccids      []string
		)
		ctx, _ = context.WithTimeout(ctx, time.Minute*3)
		if in.DataRange == model.SimCardSyncDataPartOf {
			for _, v := range in.Data {
				ebikeRequest.EbikeIds = append(ebikeRequest.EbikeIds, v.EbikeId)
			}
			ebikeRequest.Size = len(ebikeRequest.EbikeIds)
		}

		_, respSimCards, err := a.iSimCard.List(ctx, ebikeRequest, model.QueryOptions{OnlyList: true})
		if err != nil {
			return err
		}

		for _, v := range respSimCards {
			iccid2IdMap[v.Iccid] = v.Id
			iccids = append(iccids, v.Iccid)
		}

		_ = a.syncCardConsumptionInfo(ctx, iccid2IdMap, iccids...)

		if in.DataRange == model.SimCardSyncDataPartOf {
			break
		}

		if in.DataRange == model.SimCardSyncDataAll {
			if len(respSimCards) < ebikeRequest.Size {
				break
			}
			ebikeRequest.Index = ebikeRequest.Index + 1
		}
	}

	return nil
}

// List
func (a *simCard) List(ctx context.Context, in *model.SimCardListRequest, opts ...model.QueryOptions) (*model.SimCardListResponse, error) {
	var (
		err   error
		total int
		list  []*entity.SimCard
		out   = &model.SimCardListResponse{}
		opt   = model.GetQueryOption(opts...)
	)

	if in.Expiring != nil {

		now := carbon.Now(carbon.PRC)
		if *in.Expiring {
			opt.Conditions = []string{
				fmt.Sprintf("(expired_date_unix < %v AND expired_date_unix > %v)", now.AddDays(model.PendingInActiveTolerateDays).Timestamp(), now.Timestamp()),
			}
		} else {
			opt.Conditions = []string{
				fmt.Sprintf("expired_date_unix >= %v", now.AddDays(model.PendingInActiveTolerateDays).Timestamp()),
			}
		}
	}

	if len(in.ExpiredDateRange) > 0 {
		opt.Conditions = []string{
			fmt.Sprintf("(expired_date_unix >= %v AND expired_date_unix <= %v)", in.ExpiredDateRange[0], in.ExpiredDateRange[1]),
		}
	}

	if total, list, err = a.iSimCard.List(ctx, in, opt); err != nil {
		return nil, err
	}

	out.Total = total
	out.List = mapping.SimCardsEntityToDto(list)

	return out, nil
}

// Find
func (a *simCard) Find(ctx context.Context, in *model.SimCardInfoRequest) (*model.SimCardInfo, error) {
	var (
		err  error
		data *entity.SimCard
		out  = &model.SimCardInfo{}
	)

	if data, err = a.iSimCard.Find(ctx, in); err != nil {
		return nil, err
	}

	out = mapping.SimCardEntityToDto(data)
	return out, nil
}

// buildSimCard build entity
func buildSimCard(in *model.SimCardCreateRequest) *entity.SimCard {

	ety := &entity.SimCard{}

	ety.Id = in.Id

	ety.UserId = in.UserId
	ety.UserPhone = in.UserPhone

	ety.AgentId = in.AgentId
	//ety.AgentDetail = in.AgentDetail
	agentUpiot := model.AgentUPIOT
	aDetail := &model.AgentDetail{
		Name: agentUpiot.Name(),
		Code: string(agentUpiot),
	}
	ety.AgentDetail, _ = json.Marshal(aDetail)

	ety.EbikeId = in.EbikeId

	ety.Sn = in.Sn

	ety.FrameCode = in.FrameCode

	ety.Iccid = in.Iccid

	ety.EffectiveDate = in.EffectiveDate

	ety.ExpiredDate = in.ExpiredDate

	er := model.EffectiveRange{
		EffectiveDate: in.EffectiveDate,
		ExpiredDate:   in.ExpiredDate,
	}
	ety.EffectiveDateUnix, ety.ExpiredDateUnix = er.ToUnix(model.AgentUPIOT)

	ety.CreatedAt = time.Now().Unix()

	ety.UpdatedAt = in.UpdatedAt

	return ety
}
